/*
 * IContact.java
 * 
 * Version information
 * 
 * Mar 31, 2006
 * 
 * Copyright (c) AmSoft Systems, 2006
 */
package net.amsoft.iservice.icontact.service;

import static net.amsoft.iservice.icontact.util.IContactConstants.ACTIVE;
import static net.amsoft.iservice.icontact.util.IContactConstants.TYPE_ACTIVATED_SEPARATE;
import static net.amsoft.iservice.icontact.util.IContactConstants.TYPE_GROUP_ACTIVATED;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.amsoft.iservice.icontact.hibernate.ContactPage;
import net.amsoft.iservice.icontact.hibernate.IBroker;
import net.amsoft.iservice.icontact.hibernate.Message;
import net.amsoft.iservice.icontact.hibernate.Subscriber;
import net.amsoft.iservice.icontact.hibernate.Synonym;
import net.amsoft.iservice.icontact.hibernate.dataObject.LoginData;
import net.amsoft.iservice.icontact.hibernate.dataObject.MessageData;
import net.amsoft.iservice.icontact.util.IContactConstants;
import net.amsoft.iservice.icontact.util.IContactHelpers;
import net.amsoft.iservice.util.HibernateUtil;
import net.amsoft.iservice.util.IServiceUtil;
import net.amsoft.iservice.util.exception.IServiceException;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Expression;

/**
 * IContact class is designed to perform the logic operations. It performs
 * different operations based on the requests from the application layer, these
 * operations are in terms of interacting with database(saving, retreiving,
 * updating data) using the IContactHelpers class. Hence the class is a major
 * functional unit between application layer and the data (hibernate) layer. The
 * APIs provided in the IContact class are used in the application layer of the
 * IContact Service.
 * 
 */
public class IContact {
	private static final Logger oLogger = Logger.getLogger(IContact.class);

	/**
	 * Saves a new message to database, the new message is a new request. It
	 * saves the sender information and the request data to database. It sets a
	 * state for the message which can be pending, active or deleted. A pending
	 * state becomes active after the sender verifies his email or i-name and
	 * the active state becomes deleted when reciever deletes a message.
	 * 
	 * @param oSynonym
	 *            Synonym object to which the request is placed
	 * @param sSenderName
	 *            Name of the sender
	 * @param sSenderIName
	 *            I-name of the sender
	 * @param sSenderEmail
	 *            Email of the sender
	 * @param sText
	 *            Message text entered by the sender
	 * @param sHash
	 *            Hash value for this message
	 * @param iState
	 *            State of the message
	 * @param oSession
	 *            Hibernate Session to keep track of current operations
	 * @return Object of Message which is saved
	 * @throws IServiceException
	 */
	public static Message addRequest(Synonym oSynonym, String sSenderName,
			String sSenderIName, String sSenderEmail, String sText,
			String sHash, int iState, Session oSession)
			throws IServiceException {
		oLogger.info("addRequest() : entry with data : Synonym=" + oSynonym
				+ " ,SenderName=" + sSenderName + ", Sender IName="
				+ sSenderIName + ", SenderEmail=" + sSenderEmail + ", Text="
				+ sText + ", Hash=" + sHash + ", State=" + iState);
		Message oMessage = new Message();
		oMessage.setSenderName(sSenderName);
		oMessage.setSenderIName(sSenderIName);
		oMessage.setSenderEMail(sSenderEmail);
		oMessage.getText().add(sText);
		oMessage.setSynonym(oSynonym);
		oMessage.setHash(sHash);
		oMessage.setState(iState);
		oMessage.insert();
		oSession.save(oMessage);
		oLogger.info("addRequest() : exit, return Message: " + oMessage);
		return (oMessage);
	}

	/**
	 * Adds a new reply message to an existing request message. This new message
	 * is added as the child of the existing request message so that all the
	 * replies can be tracked through the parent request message.
	 * 
	 * @param oSynonym
	 *            Ssynonym object to which the request is placed
	 * @param oMessage
	 *            Parent message to which the current reply message is added
	 * @param sSenderName
	 *            Name of the sender
	 * @param sSenderIName
	 *            I-name of the sender
	 * @param sSenderEmail
	 *            Email of the sender
	 * @param sHash
	 *            Hash value for the current message
	 * @param sText
	 *            Message text entered by the sender
	 * @param iState
	 *            State of the messsage
	 * @param oSession
	 *            Hibernate Session
	 * @return object of Message which is saved
	 */
	public static Message addReply(Synonym oSynonym, Message oMessage,
			String sSenderName, String sSenderIName, String sSenderEmail,
			String sHash, String sText, int iState, Session oSession) {
		oLogger.info("addReply() : entry with data : Synonym=" + oSynonym
				+ ", Message=" + oMessage + ", SenderName=" + sSenderName
				+ ", Sender IName=" + sSenderIName + ", SenderEmail="
				+ sSenderEmail + ", Text=" + sText + ", Hash=" + sHash
				+ ", State=" + iState);
		oSynonym.update();
		oMessage.update();
		Message oReply = new Message();
		oReply.setSenderName(sSenderName);
		oReply.setSenderIName(sSenderIName);
		oReply.setSenderEMail(sSenderEmail);
		oReply.getText().add(sText);
		oReply.setSynonym(oSynonym);
		oReply.setParent(oMessage);
		oReply.setHash(sHash);
		oReply.setState(iState);
		oReply.insert();
		oSession.save(oReply);
		oLogger.info("addReply() : exit, return Reply: " + oReply);
		return (oReply);
	}

	/**
	 * API remove the given message which is marked for deletion by the reciever
	 * synonym. This API selects all the child messages of the given Message,
	 * All these messages are not deleted physically from the databse but their
	 * state is changed to INACTIVE, and they are treated as deleted messages.
	 * 
	 * @param oMessage
	 *            Parent message to be deleted
	 */
	public static void removeMessage(Message oMessage) {
		oLogger.info("removeMessage() : entry with data : Message=" + oMessage);
		Session oSession = HibernateUtil.getSessionFactory().openSession();
		Transaction oTransaction = oSession.beginTransaction();
		try {
			List lMessages = oSession.createCriteria(Message.class).add(
					Expression.eq("parent", oMessage)).add(
					Expression.eq("state", Integer
							.valueOf(IContactConstants.ACTIVE))).list();
			for (int i = 0; i < lMessages.size(); i++)
				((Message) lMessages.get(i)).remove();

			Message oMsg = (Message) oSession.createCriteria(Message.class)
					.add(Expression.eq("hash", oMessage.getHash().toString()))
					.uniqueResult();
			oMsg.remove();
			oTransaction.commit();
			oLogger.info("removeMessage() : exit after removing message");
		} catch (Exception e) {
			oLogger.error("removeMessage() : Exception : " + e.getMessage());
			oTransaction.rollback();
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}

	}

	/**
	 * API gets the count of the child messages which were saved as replies to
	 * the given Message.
	 * 
	 * @param oMessage
	 *            Request Message, used to get the reply count
	 * @return Count of the replies for given request message.
	 * @throws IServiceException
	 */
	public static int getReplyCount(MessageData oMessage)
			throws IServiceException {
		oLogger.info("getReplyCount(): entry, fetching message count");
		Session oSession = null;
		int count = 0;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oSession.beginTransaction();
			count = IContactHelpers.fetchMsgByMsgCount(oMessage, oSession);
			oLogger.info("getReplyCount(): exit, return Count: " + count);
			return count;
		} catch (Exception e) {
			oLogger.warn("getReplyCount() : Exception : " + e.getMessage()
					+ ", Data: MessageData:" + oMessage);
			return count;
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
	}

	/**
	 * This API is used to get the list of reply messages of the given request
	 * message. All the messages saved as replies are fetched and added to a
	 * list, and that list of reply messages is returned.
	 * 
	 * @param oMessage
	 *            Parernt request message
	 * @param offset
	 *            Start fetching from here
	 * @param limit
	 *            Total no. of messages required
	 * @return List of replies
	 * @throws IServiceException
	 */
	public static List getReplies(MessageData oMessage, int offset, int limit)
			throws IServiceException {
		oLogger.info("getReplies(): offset=" + offset + ",limit=" + limit);
		Session oSession = null;
		List oList = null;
		List<MessageData> oReplyList = new ArrayList<MessageData>();
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			Transaction oTransaction = oSession.beginTransaction();
			oList = IContactHelpers.fetchMsgByMsg(oMessage, offset, limit,
					oSession, oTransaction);
			for (Iterator iter = oList.iterator(); iter.hasNext();) {
				MessageData oReplyData = new MessageData();
				copyMessage((Message) iter.next(), oReplyData);
				oReplyList.add(oReplyData);
			}
			oLogger.info("getReplies(): exit, return ReplyList: " + oReplyList);
			return oReplyList;
		} catch (Exception e) {
			oLogger.warn("getReplies(): Exception=" + e.getMessage()
					+ ", Data: ReplyList: " + oReplyList);
			return oReplyList;
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
	}

	/**
	 * Creates a new contact page for the given synonym. A contact page allows
	 * other users to send requests to i-names.
	 * 
	 * @param sSynonym
	 *            Name of the synonym
	 * @param oContactPage
	 *            Configuration data to be saved
	 * @throws IServiceException
	 */
	public static void createContactPage(String sSynonym,
			ContactPage oContactPage) throws IServiceException {
		oLogger.info("createContactPage(): entry with Synonym=" + sSynonym);
		Session oSession = null;
		Transaction oTransaction = null;
		oSession = HibernateUtil.getSessionFactory().openSession();
		oTransaction = oSession.beginTransaction();
		if (sSynonym == null || oContactPage == null) {
			oLogger.debug("createContactPage(): throw exception: Synonym="
					+ sSynonym + ", ContactPage=" + oContactPage);
			throw new IServiceException(oLogger, 2028);
		}
		Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym, oSession);
		oSession.save(oSynonym);
		try {
			oContactPage = (ContactPage) oContactPage.insert();
			oSession.save(oContactPage);
			oSynonym.setContactPage(oContactPage);
			oTransaction.commit();
			oLogger.info("createContactPage() exit successfully");
		} catch (Exception e) {
			oLogger.warn("createContactPage() : Exception: " + e.getMessage());
		} finally {
			if (oSession != null)
				oSession.close();
		}
	}

	/**
	 * Used to get the contact page of the given synonym, a contact page for
	 * this i-name should exist in the database otherwise this API throws
	 * exception.
	 * 
	 * @param sSynonym
	 *            Name of the i-name
	 * @return Contact page of the given i-name
	 * @throws IServiceException
	 */
	public static ContactPage getContactPage(String sSynonym)
			throws IServiceException {
		oLogger.info("getContactPage(): entry with data : Synonym=" + sSynonym);
		Session oSession = null;
		oSession = HibernateUtil.getSessionFactory().openSession();
		if (sSynonym == null) {
			oLogger.warn("getContactPage(): throw exception : Synonym is "
					+ sSynonym);
			throw new IServiceException(oLogger, 2028);
		}
		try {
			Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym, oSession);
			oSynonym.update();
			ContactPage oContactPage = oSynonym.getContactPage();
			if (oContactPage == null
					|| oContactPage.getState() != IContactConstants.ACTIVE) {
				oLogger
						.warn("getContactPage(): Message : No contact page found for Synonym="
								+ oSynonym.getName());
				throw new Exception("No contact page found for Synonym="
						+ oSynonym.getName());
			} else {
				return oContactPage;
			}
		} catch (Exception e) {
			oLogger.warn("getContactPage() : Exception : " + e.getMessage());
			if (e instanceof IServiceException) {
				throw (IServiceException) e;
			} else {
				// contact page does not exist
				throw new IServiceException(oLogger, 2100);
			}
		} finally {
			if (oSession != null)
				oSession.close();
		}
	}

	/**
	 * Removes the contact page of the given synonym if it exists otherwise
	 * throws an exception. The contact page is not removed physically from the
	 * database, only the state is changed to INACTIVE and it is detached from
	 * the synonym.
	 * 
	 * @param sSynonym
	 *            Name of the i-name
	 * @throws IServiceException
	 */
	public static void removeContactPage(String sSynonym)
			throws IServiceException {
		oLogger.info("removeContactPage(): entry with data : Synonym="
				+ sSynonym);
		if (sSynonym == null) {
			oLogger.warn("removeContactPage(): throw exception : Synonym is "
					+ sSynonym);
			throw new IServiceException(oLogger, 2028);
		}
		Session oSession = null;
		Transaction oTransaction = null;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oTransaction = oSession.beginTransaction();
			Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym, oSession);
			ContactPage oContactPage = oSynonym.getContactPage();
			if (oSynonym.getContactPage() == null) {
				rollback(oSession, oTransaction);
				oLogger
						.debug("removeContactPage(): throw exception : ContactPage of "
								+ oSynonym.getName()
								+ " is "
								+ oSynonym.getContactPage());
				throw new IServiceException();
			} else {
				oSynonym.setContactPage(null);
				oContactPage.remove();
				oTransaction.commit();
			}
		} catch (Exception e) {
			oLogger.warn("removeContactPage(): Exception :" + e.getMessage());
			oTransaction.rollback();
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
	}

	/**
	 * Updates the contact page of the given synonym. All the new values are
	 * saved to the old contact page.
	 * 
	 * @param sSynonym
	 *            Name of the i-name
	 * @param contactPage
	 *            Contains the new configuration for the contact page
	 * @throws IServiceException
	 */
	public static void updateContactPage(String sSynonym,
			ContactPage contactPage) throws IServiceException {
		oLogger.info("updateContactPage(): entry with data : Synonym="
				+ sSynonym + ", ContactPage=" + contactPage);
		if (sSynonym == null || contactPage == null) {
			oLogger.debug("updateContactPage(): throw exception : Synonym: "
					+ sSynonym + ", ContactPage " + contactPage);
			throw new IServiceException(oLogger, 2028);
		}
		Session oSession = null;
		Transaction oTransaction = null;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oTransaction = oSession.beginTransaction();
			Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym, oSession);
			oSynonym.update();
			ContactPage oContactPage = oSynonym.getContactPage();
			oContactPage.update();
			contactPage.update();
			oContactPage.setDisplayMsg(contactPage.getDisplayMsg());
			oContactPage.setDisplayName(contactPage.getDisplayName());
			oContactPage.setEmail(contactPage.getEmail());
			oContactPage.setNotificationEmailMsg(contactPage
					.getNotificationEmailMsg());
			oContactPage.setNotificationEmailSub(contactPage
					.getNotificationEmailSub());
			oContactPage.setTags(contactPage.getTags());
			oContactPage.setVerificationEmailMsg(contactPage
					.getVerificationEmailMsg());
			oContactPage.setVerificationEmailSub(contactPage
					.getVerificationEmailSub());
			oContactPage.setVerificationType(contactPage.getVerificationType());

			oContactPage.update();
			oTransaction.commit();
		} catch (Exception e) {
			oLogger.warn("updateContactPage(): Exception :" + e.getMessage()
					+ ", ContactPage not updated");
			oTransaction.rollback();
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
	}

	/**
	 * API checks if the given synonym is subscribed or not. It returns true if
	 * synonym is already subscribed, otherwise returns false.
	 * 
	 * @param sSynonym
	 *            Name of the synonym
	 * @param sGlobalInumber
	 *            Global I-Number of the synonym
	 * @param sIbrokerName
	 *            Name of the broker to which given synonym is subscribed
	 * @return true if synonym is subscribed, false otherwise.
	 * @throws IServiceException
	 */
	public static boolean isSynonymSubscribed(String sSynonym,
			String sGlobalInumber, String sIbrokerName)
			throws IServiceException {
		oLogger.info("isSynonymSubscribed(): Entry with data : Synonym="
				+ sSynonym + ", GIN=" + sGlobalInumber + ", IBN="
				+ sIbrokerName);
		if (sSynonym == null) {
			oLogger.debug("isSynonymSubscribed(): throw exception : Synonym: "
					+ sSynonym);
			throw new IServiceException(oLogger, 2028);
		}
		Session oSession = null;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oSession.beginTransaction();
			Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym, oSession);
			oSynonym.update();
			if (oSynonym != null
					&& oSynonym.getName().equals(sGlobalInumber)
					&& oSynonym.getSubscriber().getBroker().getServerName()
							.equals(sIbrokerName)) {
				oLogger.info("isSynonymSubscribed(): exit with true");
				return true;
			} else if (oSynonym != null
					&& oSynonym.getGlobalINumber().getName().equals(
							sGlobalInumber)
					&& oSynonym.getSubscriber().getBroker().getServerName()
							.equals(sIbrokerName)) {
				oLogger.info("isSynonymSubscribed(): exit with true");
				return true;
			} else {
				oLogger.warn("isSynonymSubscribed(" + sSynonym + ","
						+ sGlobalInumber + "," + sIbrokerName
						+ ") returns false");
				oLogger.info("isSynonymSubscribed(): exit with false");
				return false;
			}
		} catch (Exception e) {
			oLogger
					.warn("isSynonymSubscribed(): Exception : "
							+ e.getMessage());
			return false;
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
	}

	/**
	 * Gets a hashmap of contact page of the synonym and the synonym list.
	 * 
	 * @param oSynList
	 *            List of synonym
	 * @return Hashmap having contact page id and synonym as key-value pair.
	 * @throws IServiceException
	 */
	public static HashMap getSynonymGroups(ArrayList oSynList)
			throws IServiceException {
		oLogger.info("getSynonymGroups(): entry");
		long id[] = new long[oSynList.size()];
		for (int i = 0; i < oSynList.size(); i++) {
			try {
				id[i] = getContactPage((String) oSynList.get(i)).update()
						.getId();
			} catch (Exception e) {
				oLogger.warn("getSynonymGroups(): Exception: " + e.getMessage()
						+ ", Set " + i + "th id=0");
				id[i] = 0;
			}
		}
		HashMap groupMap = makeGroup(id, oSynList);
		oLogger.info("getSynonymGroups(): exit, return GroupMap");
		return groupMap;
	}

	/**
	 * It is used to commit the given transaction in the given session
	 * 
	 * @param oSession
	 *            Current Hibernate Session for the transaction
	 * @param oTransaction
	 *            Transaction in the current session
	 */
	public static void commit(Session oSession, Transaction oTransaction) {
		oLogger.info("commit(): entry");
		oTransaction.commit();
		oSession.clear();
		oSession.close();
		oLogger.info("commit(): exit, completed");
	}

	/**
	 * It is used to rollback the operation in given transaction of the given
	 * session
	 * 
	 * @param oSession
	 *            Current Hibernate Session for the transaction
	 * @param oTransaction
	 *            Transaction in the current session
	 */
	public static void rollback(Session oSession, Transaction oTransaction) {
		oLogger.info("rollback(): entry");
		oTransaction.rollback();
		oSession.clear();
		oSession.close();
		oLogger.info("rollback(): exit, completed");
	}

	/**
	 * This API is used to get the information of the given IBroker.
	 * 
	 * @param sIbrokerName
	 *            Name of the IBroker server
	 * @return IBroker information in the form of Map
	 * @throws IServiceException
	 */
	public static Map getIBrokerData(String sIbrokerName)
			throws IServiceException {
		oLogger.info("getIBrokerData(): entry with data : BrokerName: "
				+ sIbrokerName);
		Map oDataMap = null;
		Session oSession = null;
		Transaction oHibernateTran = null;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oSession.beginTransaction();
			oLogger.debug("getIBrokerData(): entry with IBroker="
					+ sIbrokerName);
			IBroker oIBroker = IContactHelpers.fetchIBroker(oSession,
					oHibernateTran, sIbrokerName);
			oDataMap = oIBroker.getBrokerData();
			oLogger.info("getIBrokerData(): exit");
			return oDataMap;
		} catch (Exception e) {
			oLogger.warn("getIBrokerData(): Exception :" + e.getMessage());
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
		return oDataMap;
	}

	/**
	 * This private method creates the synonym groups
	 * 
	 * @param id
	 *            Contact page id
	 * @param oSynList
	 *            List of synonyms
	 * @return Hashmap of groups
	 */
	private static HashMap makeGroup(long id[], ArrayList oSynList) {
		oLogger.info("makeGroup(): entry");
		HashMap<Long, ArrayList<Object>> synMap = new HashMap<Long, ArrayList<Object>>();
		for (int i = 0; i < oSynList.size() - 1; i++) {
			for (int j = i + 1; j < oSynList.size(); j++) {
				if (id[i] == id[j] && id[i] != 0) {
					if (synMap.keySet().contains(id[i])) {
						if (!(synMap.get(id[i])).contains(oSynList.get(j))) {
							(synMap.get(id[i])).add(oSynList.get(j));
						}
					} else {
						ArrayList<Object> group = new ArrayList<Object>();
						group.add(oSynList.get(i));
						group.add(oSynList.get(j));
						synMap.put(id[i], group);
					}
				}
			}
		}
		for (int i = 0; i < id.length; i++) {
			if (!(synMap.keySet().contains(id[i])) && id[i] != 0) {
				ArrayList<Object> group = new ArrayList<Object>();
				group.add(oSynList.get(i));
				synMap.put(id[i], group);
			}
		}
		Set set = synMap.keySet();
		Iterator iter = set.iterator();
		String logString = "makeGroup(): ";
		while (iter.hasNext()) {
			Long i = (Long) iter.next();
			ArrayList group = (ArrayList) synMap.get(i);
			logString += "group [" + i + "]: ";
			for (int j = 0; j < group.size(); j++) {
				logString += group.get(j) + ", ";
			}
		}
		oLogger.debug(logString);
		return synMap;
	}

	/**
	 * This private method is used to copy data from one Message object to
	 * another.
	 * 
	 * @param oMessage
	 *            Source Message object
	 * @param oMsgData
	 *            Target Message object
	 * @return Target Message object with new data
	 */
	private static MessageData copyMessage(Message oMessage,
			MessageData oMsgData) {
		oLogger.info("copyMessage(): entry");
		if (oMsgData == null) {
			oMsgData = new MessageData();
		}
		oMsgData.setId(oMessage.getId());
		oMsgData.setSenderEMail(oMessage.getSenderEMail());
		oMsgData.setSenderIName(oMessage.getSenderIName());
		oMsgData.setSenderName(oMessage.getSenderName());
		oMsgData.setSynonym(oMessage.getSynonym().getName());
		oMsgData.setText((String) oMessage.getText().iterator().next());
		oMsgData.setHash(oMessage.getHash());
		oMsgData.setCreationTime(oMessage.getCreationTime());
		oMsgData.setModificationTime(oMessage.getModificationTime());
		oMsgData.setState(oMessage.getState());
		oLogger.info("copyMessage(): exit, copy complete");
		return oMsgData;
	}

	/**
	 * Gets the Broker object using its broker name. IContactHelpers class is
	 * used to fetch the correct object from database
	 * 
	 * @param sIbrokerName
	 *            Name of the broker
	 * @return Object of the Broker
	 * @throws IServiceException
	 */
	public static IBroker getIBroker(String sIbrokerName)
			throws IServiceException {
		oLogger.info("getIBroker(): entry with data : BrokerName: "
				+ sIbrokerName);
		IBroker oIBroker = null;
		Session oHibernateSession = null;
		Transaction oHibernateTran = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			oHibernateSession.beginTransaction();
			oIBroker = IContactHelpers.fetchIBroker(oHibernateSession,
					oHibernateTran, sIbrokerName);
			oLogger.info("getIBroker(): exit, return Broker: " + oIBroker);
			return oIBroker;
		} catch (Exception e) {
			oLogger.warn("getIBroker(): Exception : " + e.getMessage()
					+ ", return Broker = null");
			return oIBroker;
		} finally {
			if (oHibernateSession != null) {
				oHibernateSession.close();
			}
		}
	}

	/**
	 * Saves the message for an synonym. It first checks whether the message is
	 * a request message or a reply message and then calls the specific API to
	 * save message for the synonym
	 * 
	 * @param sSynonym
	 *            Name of the synonym
	 * @param sHash
	 *            Hash value of the current message
	 * @param sRef
	 * @param sSenderEmail
	 *            Email of the sender
	 * @param sSenderName
	 *            Name of the sender
	 * @param sSenderIname
	 *            I-name of the sender
	 * @param sMsg
	 *            Message text sent by the sender
	 * @param iState
	 *            State of the message to be saved
	 * @return Message object which is saved
	 * @throws IServiceException
	 */
	public static Message saveMessages(String sSynonym, String sHash,
			String sRef, String sSenderEmail, String sSenderName,
			String sSenderIname, String sMsg, int iState)
			throws IServiceException {
		oLogger.info("saveMessages(): entry with data : Synonym: " + sSynonym
				+ ", Hash: " + sHash + ", SenderEmail: " + sSenderEmail
				+ ", SenderIname: " + sSenderIname + ", Message: " + sMsg);
		Session oHibernateSession = null;
		Transaction oHibernateTran = null;
		Message oParent = null;
		Message oSavedMsg = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			oHibernateTran = oHibernateSession.beginTransaction();
			// saving request
			Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym,
					oHibernateSession);

			if (sRef != null && sRef.trim().length() > 0) {
				oLogger.debug("saveMessages(): Saving as Reply, REF=" + sRef);
				oParent = IContactHelpers.fetchMsgByHashAndState(sRef, ACTIVE,
						oHibernateSession);
				oParent.update();
				oSavedMsg = addReply(oSynonym, oParent, sSenderName,
						sSenderIname, sSenderEmail, sHash, sMsg, iState,
						oHibernateSession);
			} else {
				oLogger.debug("saveMessages(): Saving as Request, REF=" + sRef);
				oSavedMsg = addRequest(oSynonym, sSenderName, sSenderIname,
						sSenderEmail, sMsg, sHash, iState, oHibernateSession);
			}
			oHibernateTran.commit();
			oLogger.info("saveMessages(): exit after commit");
			return oSavedMsg;
		} catch (Exception e) {
			oHibernateTran.rollback();
			oLogger.warn("saveMessages() : Exception : " + e.getMessage()
					+ ", return saved message is: " + oSavedMsg);
			return oSavedMsg;
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * This API is used to fetch all the synonyms of a Global I-Number, and sets
	 * it back in LoginData object.
	 * 
	 * @param oLoginData
	 *            LoginData with Global I-Number
	 * @return LoginData having all synonyms of given Global I-Number
	 * @throws IServiceException
	 */
	public static LoginData loadSynonyms(LoginData oLoginData)
			throws IServiceException {
		oLogger.debug("loadSynonyms() : entry with data : GIN="
				+ oLoginData.getGlobalInumber());
		String sGlobalInumber = oLoginData.getGlobalInumber();
		Session oHibernateSession = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory()
					.getCurrentSession();
			oHibernateSession.beginTransaction();
			Subscriber oSubscriber = IContactHelpers.fetchSubscriber(
					sGlobalInumber, oHibernateSession);
			oLogger.debug("loadSynonyms(): Subscriber found");
			ArrayList<String> aSynonyms = new ArrayList<String>();
			for (Iterator itr = oSubscriber.getSynonyms().iterator(); itr
					.hasNext();) {
				aSynonyms.add(((Synonym) itr.next()).getName());
			}
			oLoginData.setAllSynonyms(aSynonyms);
			oLogger.info("loadSynonyms() : exit, returning LoginData: "
					+ oLoginData);
			return oLoginData;
		} catch (Exception e) {
			oLogger.warn("loadSynonyms() : Exception : " + e.getMessage()
					+ ", returning LoginData: " + oLoginData);
			return oLoginData;
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * Saves a reply message when parent request message is not provided, we get
	 * the request message using its hash value.
	 * 
	 * @param sHash
	 *            Hash value of the request message
	 * @param sSubject
	 *            Subject of the message
	 * @param sMsg
	 *            Message text of the message
	 * @return Message object which is saved
	 */
	public static Message saveReply(String sHash, String sSubject, String sMsg) {
		oLogger.info("saveReply() : entry with data : Hash: " + sHash
				+ ", Subject: " + sSubject + ", Message: " + sMsg);
		Session oHibernateSession = null;
		Transaction oHibernateTran = null;
		Message oParent = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			oHibernateTran = oHibernateSession.beginTransaction();
			oParent = (Message) IContactHelpers.fetchMsgByHashAndState(sHash,
					ACTIVE, oHibernateSession);
			oParent.update();
			if (oParent != null) {
				oLogger.debug("saveReply() : adding reply to Message: "
						+ oParent);
				// FIXME: subject is stored as senderName in case of replies
				addReply(oParent.getSynonym(), oParent, sSubject, oParent
						.getSynonym().getName(), "", IServiceUtil
						.generateUniqueID(), sMsg, ACTIVE, oHibernateSession);
			}
			oHibernateTran.commit();
			oLogger.info("saveReply() : exit, return Message: " + oParent);
			return oParent;
		} catch (Exception e) {
			oHibernateTran.rollback();
			oLogger.warn("saveReply() : Exception : " + e.getMessage()
					+ ", return Message: " + oParent);
			return oParent;
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * Deletes all the messages which are marked for deletion by the synonym, it
	 * selects one message at a time and uses other API to set state of that
	 * message to INACTIVE
	 * 
	 * @param aAllSyns
	 *            List of synonyms
	 * @param msgToDel
	 *            List of messages to be delted
	 * @return true if deletion is done, false otherwise
	 */
	public static boolean deleteMessages(ArrayList aAllSyns, String msgToDel[]) {
		oLogger.info("deleteMessages() : entry with data : ArrayList: "
				+ aAllSyns);
		Session oHibernateSession = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			for (int i = 0; i < msgToDel.length; i++) {
				oLogger.debug("deleteMessages() : Message to Delete: "
						+ msgToDel[i]);
				String sHash = msgToDel[i];
				Message oMsg = null;
				if (sHash != null && sHash.trim().length() > 0) {
					oMsg = (Message) IContactHelpers.fetchMsgByHashAndState(
							sHash, ACTIVE, oHibernateSession);
				}
				if (oMsg != null
						&& aAllSyns.contains(oMsg.getSynonym().getName())) {
					removeMessage(oMsg);
				}
			}
			oLogger.info("deleteMessages() : exit after deletion, return true");
			return true;
		} catch (Exception e) {
			oLogger.warn("deleteMessages() : Exception : " + e.getMessage());
			return false;
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * It gets message using its hash value and its state
	 * 
	 * @param sHash
	 *            Hash value of the message
	 * @param iState
	 *            State of the message
	 * @return object of Message
	 */
	public static Message getMessageByHashAndState(String sHash, int iState) {
		oLogger.info("getMessageByHashAndState() : entry with data : Hash: "
				+ sHash + ", State: " + iState);
		Session oHibernateSession = null;
		Transaction oHibernateTran = null;
		Message oMessage = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			oHibernateTran = oHibernateSession.beginTransaction();
			oMessage = (Message) IContactHelpers.fetchMsgByHashAndState(sHash,
					iState, oHibernateSession);
			oMessage.update();
			oHibernateTran.commit();
			oLogger.info("getMessageByHashAndState() : exit, return Message: "
					+ oMessage);
			return oMessage;
		} catch (Exception e) {
			oHibernateTran.rollback();
			oLogger.warn("getMessageByHashAndState() : Exception : "
					+ e.getMessage() + ", return Message: " + oMessage);
			return oMessage;
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * Gets the list of all messages related to given synonym
	 * 
	 * @param sSynonym
	 *            Name of the i-name
	 * @param offset
	 *            Start fetching from here
	 * @param limit
	 *            Total no. of messages required
	 * @return List of messages
	 * @throws IServiceException
	 */
	public static List getRequestsBySynonym(String sSynonym, int offset,
			int limit) throws IServiceException {
		oLogger.info("getRequests() : entry with data : Synonym=" + sSynonym
				+ ", Offset=" + offset + ", Limit=" + limit);
		List oList = null;
		List<MessageData> oMsgList = new ArrayList<MessageData>();
		Session oSession = null;
		Synonym oSynonym = null;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			Transaction oTransaction = oSession.beginTransaction();
			oSynonym = IContactHelpers.fetchSynonym(sSynonym, oSession);
			oList = IContactHelpers.fetchMsgBySyn(oSynonym, offset, limit,
					oSession, oTransaction);
			for (Iterator iter = oList.iterator(); iter.hasNext();) {
				try {
					MessageData oMsgData = new MessageData();
					copyMessage((Message) iter.next(), oMsgData);
					oMsgList.add(oMsgData);
				} catch (Exception e) {
					oLogger.warn("getRequests() : Exception : "
							+ e.getMessage() + ", Data: MessageList: "
							+ oMsgList);
				}
			}
			oLogger.info("getRequests() : exit, return MessageList: "
					+ oMsgList);
			return oMsgList;
		} catch (Exception e) {
			oLogger.warn("getRequests() : Exception : " + e.getMessage()
					+ ", +Data: Synonym=" + oSynonym.getName() + ", Offset="
					+ offset + ", Limit=" + limit);
			return oMsgList;
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
	}

	/**
	 * Gets count of the messages that are saved for the given synonym
	 * 
	 * @param sSynonym
	 *            Name of the synonym
	 * @return Count of the messages
	 */
	public static int getMsgCountBySynonym(String sSynonym) {
		oLogger.info("getRequests() : entry with data : Synonym=" + sSynonym);
		Session oSession = null;
		Synonym oSynonym = null;
		int iMsgCount = 0;
		try {
			oSession = HibernateUtil.getSessionFactory().openSession();
			oSynonym = IContactHelpers.fetchSynonym(sSynonym, oSession);
			iMsgCount = IContactHelpers.fetchMsgBySynCount(oSynonym, oSession);
			oLogger.info("getRequests() : exit, return MessageList: "
					+ iMsgCount);
			return iMsgCount;
		} catch (Exception e) {
			oLogger.warn("getRequests() : Exception : " + e.getMessage()
					+ ", +Data: Synonym=" + oSynonym.getName());
			return iMsgCount;
		} finally {
			if (oSession != null) {
				oSession.close();
			}
		}
	}

	/**
	 * This API is used to update the state of a message. Generally used to
	 * update state from PENDING to ACTIVE. State PENDING is set when sender
	 * sends over a request, this request becomes ACTIVE only when sender
	 * verifies its email or i-name.
	 * 
	 * @param sHash
	 *            Hash value of the message
	 * @param iOldState
	 *            Old state of the message
	 * @param iNewState
	 *            New state of the message
	 * @return Message object with updated state
	 */
	public static Message updateMessageState(String sHash, int iOldState,
			int iNewState) {
		oLogger.info("updateMessageState() : entry with data : Hash: " + sHash
				+ ", Old State: " + iOldState + ", New State: " + iNewState);
		Session oHibernateSession = null;
		Transaction oHibernateTran = null;
		Message oMessage = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			oHibernateTran = oHibernateSession.beginTransaction();
			oMessage = IContactHelpers.fetchMsgByHashAndState(sHash, iOldState,
					oHibernateSession);
			oMessage.setState(iNewState);
			oHibernateSession.save(oMessage);
			oHibernateTran.commit();
			oMessage.getSynonym().getContactPage().update();
			Set oText = oMessage.getText();
			oLogger.debug("updateMessageState() : Message Text: "
					+ oText.toString());
			oLogger
					.info("updateMessageState() : exit after updating state of Message: "
							+ oMessage);
			return oMessage;
		} catch (HibernateException e) {
			oHibernateTran.rollback();
			oLogger.warn("updateMessageState() : Exception : " + e.getMessage()
					+ ", return Message: " + oMessage);
			return oMessage;
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * Activates group of synonyms which share a common contact page. The same
	 * contact page is shown for all the synonyms which are in a group
	 * 
	 * @param sSameAsSyn
	 *            Synonym with a contact page
	 * @param lAllSyns
	 *            List of the synonyms
	 * @param sSynonym
	 *            Name of the synonym to be grouped with sSameAsSyn
	 * @param sLastType
	 *            Type of activation
	 * @return true if grouped successfully, false otherwise
	 */
	public static boolean activateGroup(String sSameAsSyn, ArrayList lAllSyns,
			String sSynonym, String sLastType) {
		oLogger.info("activateGroup() : entry with data : SameAsSyn: "
				+ sSameAsSyn + ", ArrayList: " + lAllSyns + ", Synonym: "
				+ sSynonym + ", LastType: " + sLastType);
		Session oHibernateSession = null;
		Transaction oHibernateTrans = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			if (lAllSyns.contains(sSameAsSyn)) {
				oHibernateTrans = oHibernateSession.beginTransaction();
				ContactPage oContactPage = ((Synonym) IContactHelpers
						.fetchSynonym(sSameAsSyn, oHibernateSession).update())
						.getContactPage();
				Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym,
						oHibernateSession);
				if (sLastType.equals(TYPE_ACTIVATED_SEPARATE)) {
					oLogger
							.debug("activateGroup() : Separate to Group, Last Type: "
									+ sLastType);
					oSynonym.getContactPage().remove();
				}
				oLogger.debug("activateGroup() : Deactivate to Group");
				oHibernateSession.save(oContactPage);
				oSynonym.setContactPage(oContactPage);
				oHibernateSession.save(oSynonym);
				oHibernateTrans.commit();
			}
			oLogger.info("activateGroup() : exit, return true");
			return true;
		} catch (IServiceException e) {
			oHibernateTrans.rollback();
			oLogger.warn("activateGroup() : Exception : " + e.getMessage()
					+ ", return false");
			return false;
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * It deactivates a group. If the synonym is not sharing the contact page,
	 * the state of page is changed to INACTIVE and is detached from the
	 * synonym, otherwise simply detach contact page from synonym
	 * 
	 * @param sLastType
	 *            Type of activation
	 * @param sSynonym
	 *            Name of the synonym
	 */
	public static void deactivateGroup(String sLastType, String sSynonym) {
		oLogger.info("deactivateGroup() : entry with data : Last Type: "
				+ sLastType + ", Synonym: " + sSynonym);
		Session oHibernateSession = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			if (sLastType.equals(TYPE_ACTIVATED_SEPARATE)) {
				oLogger
						.debug("deactivateGroup() : deactivating - separate contact page");
				removeContactPage(sSynonym);
			} else if (sLastType.equals(TYPE_GROUP_ACTIVATED)) {
				Transaction oHibernateTran = oHibernateSession
						.beginTransaction();
				Synonym oSynonym = (Synonym) IContactHelpers.fetchSynonym(
						sSynonym, oHibernateSession).update();
				oLogger
						.debug("deactivateGroup() : deactivating - group contact page for "
								+ oSynonym.getName());
				oSynonym.setContactPage(null);
				oHibernateSession.save(oSynonym);
				oHibernateTran.commit();
			}
			oLogger.info("deactivateGroup() : exit after commit");
		} catch (IServiceException e) {
			oLogger.info("deactivateGroup() : Exception : " + e.getMessage()
					+ ", Deactivation of Contact Page failed");
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * Edits a contact page shared by a group of synonyms
	 * 
	 * @param sSynonym
	 *            Name of the synonym
	 * @param oContactPage
	 *            Shared contact page
	 */
	public static void editGroupPage(String sSynonym, ContactPage oContactPage) {
		oLogger.info("editGroupPage() : entry with data : Synonym: " + sSynonym
				+ ", ContactPage: " + oContactPage);
		Session oHibernateSession = null;
		try {
			oHibernateSession = HibernateUtil.getSessionFactory().openSession();
			Transaction oHibernateTrans = oHibernateSession.beginTransaction();
			Synonym oSynonym = IContactHelpers.fetchSynonym(sSynonym,
					oHibernateSession);
			ContactPage oSynContactPage = oSynonym.getContactPage();
			copyProperties(oContactPage, oSynContactPage);
			oSynonym.setContactPage(oSynContactPage);
			oHibernateSession.save(oSynonym);
			oHibernateTrans.commit();
			oLogger.info("editGroupPage() : exit after updating contact page");
		} catch (IServiceException e) {
			oLogger.warn("editGroupPage() : Exception : " + e.getMessage()
					+ ", contact page updation failed");
		} finally {
			if (oHibernateSession != null)
				oHibernateSession.close();
		}
	}

	/**
	 * This private method is used to copy the data from one ContactPage Object
	 * to another
	 * 
	 * @param oContactPage
	 *            Source ContactPage object
	 * @param oSynContactPage
	 *            Target ContactPage object
	 */
	private static void copyProperties(ContactPage oContactPage,
			ContactPage oSynContactPage) {
		oLogger.info("copyProperties() : entry");
		oSynContactPage.setDisplayMsg(oContactPage.getDisplayMsg());
		oSynContactPage.setDisplayName(oContactPage.getDisplayName());
		oSynContactPage.setNotificationEmailMsg(oContactPage
				.getNotificationEmailMsg());
		oSynContactPage.setNotificationEmailSub(oContactPage
				.getNotificationEmailSub());
		oSynContactPage.setTags(oContactPage.getTags());
		oSynContactPage.setVerificationEmailMsg(oContactPage
				.getVerificationEmailMsg());
		oSynContactPage.setVerificationEmailSub(oContactPage
				.getVerificationEmailSub());
		oSynContactPage.setEmail(oContactPage.getEmail());
		oSynContactPage.setVerificationType(oContactPage.getVerificationType());
		oLogger.info("copyProperties() : exit");
	}
}